#include <iostream>

#include <nemesis.h>
#include <nemesis.dbms.h>
#include <nemesis.app.h>

#include <nemesis.xml.Compiler.h>
#include <nemesis.xml.Node.h>

#include <nemesis.dbos.Repository.h>

#include <nemesis.dbms.oracle.Database.h>

#include <dbos_demo.oodb.Table01.h>
#include <dbos_demo.oodb.Table02.h>
#include <dbos_demo.oodb.Table03.h>

#include <dbos_demo.phase.Populate.h>
#include <dbos_demo.phase.Instanciate.h>
#include <dbos_demo.phase.UseOneByOne.h>
#include <dbos_demo.phase.Unload.h>
#include <dbos_demo.phase.CanNotUseIncoherent.h>
#include <dbos_demo.phase.CanNotUnload.h>
#include <dbos_demo.phase.CanNotReload.h>

using namespace dbos_demo;

class TestStorageArea : public Application {
public:
   typedef std::vector <phase::Base*>::iterator phase_iterator;

   TestStorageArea ();

   phase_iterator phase_begin () throw () { return a_phases.begin (); }
   phase_iterator phase_end () throw () { return a_phases.end (); }
   
   static phase::Base* phase (phase_iterator& ii) throw () { return *ii; }
   
private:
   nemesis::dbms::oracle::Database* a_db;
   std::vector <phase::Base*> a_phases;
   dbos::Repository a_repository;
   phase::Base* a_useAfterClear;

   void initialize () throw (RuntimeException); 
   void run () throw (RuntimeException);      
};

using namespace std;

int main (int argc, const char** argv)
{
   CommandLine& commandLine (CommandLine::instanciate ());
   TestStorageArea oodbNull;

   try {
      commandLine.initialize (argv, argc);
      commandLine.verify ();

      Logger::setLevel (Logger::Local6); 
      Logger::initialize ("dbos", new TraceWriter ("dbos.trace", 4 * 1024 * 1024));
 
      oodbNull.start ();
   }
   catch (Exception& ex) {
      cout << ex.asString () << endl;
   }
   
   return 0;
}

TestStorageArea::TestStorageArea () : 
   Application ("dbos_demo", "Probador de NemesisRD.dbos", "1.0.0"),
   a_repository ("dbos_demo")
{
   CommandLine& cl (CommandLine::instanciate ());
      
   cl.add ("user", CommandLine::Argument::Mandatory, "Nombre del usuario");
   cl.add ("password", CommandLine::Argument::Mandatory, "Clave del usuario");
   cl.add ("db", CommandLine::Argument::Optional, "Nombre de la base de datos");
}

void TestStorageArea::initialize ()
   throw (RuntimeException)
{
   a_db = new nemesis::dbms::oracle::Database (CommandLine::instanciate ().getValue ("db"));   

   oodb::Table01::setup (*a_db, a_repository);
   oodb::Table02::setup (*a_db, a_repository);
   oodb::Table03::setup (*a_db, a_repository);
   
   a_phases.push_back (new phase::Populate);
   a_phases.push_back (new phase::Instanciate);
   a_phases.push_back (a_useAfterClear = new phase::UseOneByOne);
   a_phases.push_back (new phase::Unload);
   a_phases.push_back (new phase::CanNotUseIncoherent);
   a_phases.push_back (new phase::CanNotUnload);
   a_phases.push_back (new phase::CanNotReload);
}

void TestStorageArea::run () 
   throw (RuntimeException)
{
   LOGMETHOD (TraceMethod tm ("TestStorageArea", "run", FILE_LOCATION)); 

   CommandLine& cl (CommandLine::instanciate ());  

   try {      
      Connection* connection = a_db->createConnection ("dbos_demo", cl.getValue ("user"), cl.getValue ("password"));

      Guard guard (connection);

      for (phase_iterator ii = phase_begin (), maxii = phase_end (); ii != maxii; ii ++)
         phase (ii)->apply (*connection);      
         
      a_repository.clear ();
      a_useAfterClear->apply (*connection);      
   }
   catch (dbms::DatabaseException& edb) {
      throw RuntimeException (edb); 
   }

   xml::Compiler report;
   xml::Node root ("dbos_demo");
   cout << "Resumen: " << endl << report.apply (a_repository.asXML (&root)) << endl;
   
   writeContext ("dbos.context");
}

